package top.ajunnihao.core.web.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.multipart.MultipartFile;
import top.ajunnihao.core.entity.User;
import top.ajunnihao.core.entity.UserAbility;
import top.ajunnihao.core.service.UserAbilityService;
import top.ajunnihao.core.service.UserService;
import top.ajunnihao.core.spring.exception.CommonException;
import top.ajunnihao.core.util.AJunUtils;
import top.ajunnihao.core.util.FileStore;
import top.ajunnihao.core.util.resp.JResp;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * TODO 用户控制器
 *
 * @author AJun
 * @version 1.0.0
 * @date 2020/5/24
 */
@Controller
public class UserController {

    private static final String CAPTCHA_CODE = "CAPTCHA_CODE";
    private static final String USER = "USER";
    private static final String USER_ABILITY = "USER_ABILITY";
    private static final String LOGIN_USERNAME = "loginUsername";
    private static final String LOGIN_USER_PWD = "loginUserPwd";
    private static final int AUTO_LOGIN_DAYS = 7 * 24 * 60 * 60; // 自动登录天数(登陆后七天免登录)
    // public static FileStore store = new FileStore("/usr/local/ajun/bbsfile/photo/", "/bbsfile/photo/");
    public static FileStore store = new FileStore("D:/bbsfile/photo/", "/bbsfile/photo/");

    @Autowired
    private UserService userService;
    @Autowired
    private UserAbilityService userAbilityService;

    /**
     * 用户注册页
     */
    @GetMapping("/register")
    public String register() {
        return "user/register";
    }

    /**
     * 用户登录页
     */
    @GetMapping("/login")
    public String login(@RequestParam(value = "ret", required = false) String retUrl, Model model) {
        if (retUrl == null) {
            retUrl = "";
        }
        model.addAttribute("retUrl", retUrl);
        return "user/login";
    }

    /**
     * 用户信息页
     */
    @GetMapping("/u/user/info")
    public String userInfo(@SessionAttribute(value = USER) User user,
                           @SessionAttribute(value = USER_ABILITY) UserAbility userAbility,
                           Model model) {
        model.addAttribute("user", user);
        model.addAttribute("userAbility", userAbility);
        return "user/info";
    }

    /**
     * 删除用户，永久注销 TODO
     */
    @PostMapping("/u/user/info/delUser.do")
    @ResponseBody
    public Object delUser() {
        return JResp.success();
    }

    /**
     * 更改用户昵称
     */
    @PostMapping("/u/user/info/updUserName.do")
    @ResponseBody
    public Object updUserName(@RequestBody JSONObject jReq,
                              @SessionAttribute(value = USER) User user) {
        String name = jReq.getString("name"); // 用户昵称
        if (StringUtils.isEmpty(name)) {
            return JResp.fail("请输入新昵称");
        }
        // 拷贝一份，不修改 session 的 user
        User u = new User();
        BeanUtil.copyProperties(user, u);
        u.setName(name);
        boolean okFlag;
        try {
            okFlag = userService.updateUser(u);
        } catch (DuplicateKeyException e) {
            throw new CommonException("用户名已存在");
        }
        if (okFlag) {
            return JResp.success();
        }
        return JResp.fail("更改失败");
    }

    /**
     * 上传用户头像
     */
    @PostMapping("/u/user/info/updateAvatar.do")
    @ResponseBody
    public Object uploadAvatar(@RequestParam("file") MultipartFile file,
                               @SessionAttribute(value = USER) User user,
                               HttpServletRequest request) {
        Map<String, Object> result = new HashMap<String, Object>();
        if (file != null && !file.isEmpty()) {
            String realName = file.getOriginalFilename(); // 文件原始名称
            if (StringUtils.isEmpty(realName)) {
                return JResp.fail(result, "请上传文件");
            }
            String suffix = AJunUtils.getSuffix(realName); // 文件后缀
            String tmpName = AJunUtils.guid2() + suffix; // 临时文件名称

            // 项目真实路径
            File realDir = new File(request.getServletContext().getRealPath("/tmp"));
            realDir.mkdirs();
            // 临时文件目录
            File tmpFile = new File(realDir, tmpName);

            // 上传文件
            try {
                file.transferTo(tmpFile);
                if (tmpFile.length() > 3000000) {
                    return JResp.fail(result, "图片太大!需小于3M!");
                }

                // 头像正式 url
                String url = usePhoto(user, tmpFile);

                // 回应给客户端的信息
                String tmpUrl = request.getContextPath() + "/tmp/" + tmpName; // 临时文件访问 url
                result.put("realName", realName); // 文件原始名称
                result.put("tmpName", tmpName); // 临时文件名称
                result.put("tmpUrl", tmpUrl); // 临时文件访问 url
                result.put("url", url); // 真实访问 url
                return JResp.success(result, "上传成功");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return JResp.fail(result, "上传失败");
        }
        return JResp.fail(result, "请上传文件");// /usr/local/ajun/bbsfile
    }

    /**
     * 用户头像更改，图片裁剪，url 处理
     */
    private String usePhoto(User user, File tmpImage) {
        // 示例： 000/1_15728601766396.jpg
        String path = String.format("%03d/%d_%s.jpg", user.getId() / 1000, user.getId(), AJunUtils.guid2());

        // 先删除旧的头像
        if (user.getThumb() != null && user.getThumb().length() > 0) {
            // 根据头像的 url 找到图片真实路径
            File oldFile = store.fileOfUrl(user.getThumb());
            try {
                FileUtils.deleteQuietly(oldFile);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 保存新的头像图片
        File dstFile = store.getFile(path);
        // 创建层次目录
        dstFile.getParentFile().mkdirs();

        // 图片裁剪为正方形，并将图片移动到真实文件夹
        clipPhoto(tmpImage, dstFile);

        // 头像的正式URL
        String url = store.getUrl(path);
        user.setThumb(url);

        // 保存到 user 表
        boolean okFlag = userService.updateUser(user);
        if (okFlag) {
            return url;
        }
        return "";
    }

    /**
     * 图片裁剪: 用到了 huTool 工具包
     */
    private void clipPhoto(File srcFile, File dstFile) {
        // 读取源图，缩小为100x100 (最大尺寸)
        BufferedImage srcImg = ImgUtil.read(srcFile);
        Image img = ImgUtil.scale(srcImg, 100, 100);

        // 截取中间的正方形
        int w = img.getWidth(null);
        int h = img.getHeight(null);
        int size = Math.min(w, h); // 取最小值
        int x = (w - size) / 2;
        int y = (h - size) / 2;

        // 截取
        ImgUtil.cut(img, dstFile, new Rectangle(x, y, size, size));
    }

    /**
     * 用户注册 Rest 服务
     */
    @PostMapping("/register.do")
    @ResponseBody
    public Object register(HttpSession session, @RequestBody JSONObject jReq) {
        // 获取用户输入
        String name = jReq.getString("name").trim();
        String email = jReq.getString("email").trim();
        String password = jReq.getString("password").trim();
        String captcha = jReq.getString("captcha").trim(); // 验证码

        // 验证码校验
        if (StrUtil.isNotEmpty(captcha)) {
            String realCaptchaCode = (String) session.getAttribute(CAPTCHA_CODE);
            // 使用完毕就删除，防止重复使用
            session.removeAttribute(CAPTCHA_CODE);
            // 忽略大小写比较
            if (!realCaptchaCode.equalsIgnoreCase(captcha)) {
                return JResp.fail("验证码错误");
            }
        }

        // 封装对象
        User user = new User();
        user.setName(name);
        user.setEmail(email);
        user.setPassword(SecureUtil.md5(password)); // md5 加密存储
        user.setTimeCreate(new Date());
        user.setTimeUpdate(new Date());
        user.setEmailFlag(true);

        // 数据库查询用户，看是否用户名重复
        User userByName = userService.findUserByName(user.getName());
        if (userByName != null) {
            return JResp.fail("用户名已被占用");
        }

        // 调用 Service 存储用户信息
        boolean saveOkFlag = userService.saveUser(user);
        if (!saveOkFlag) {
            return JResp.fail("注册失败");
        }
        // 存储用户权限
        UserAbility userAbility = new UserAbility();
        userAbility.setUserId(user.getId());
        userAbility.setBanFlag(0);
        userAbility.setBanDate(null);
        userAbility.setImageCount(15);
        userAbility.setImageMax(15);
        userAbility.setMsgCount(5);
        userAbility.setMsgMax(5);
        userAbility.setReplyCount(20);
        userAbility.setReplyMax(20);
        // 存入数据库
        boolean okFlag = userAbilityService.saveUserAbility(userAbility);
        if (!okFlag) {
            return JResp.fail("注册失败");
        }
        // 用户登录
        this.userLogin(session, user);
        return JResp.success("注册成功");
    }

    /**
     * 用户登录 Rest 服务
     */
    @PostMapping("/login.do")
    @ResponseBody
    public Object login(@RequestBody JSONObject jReq,
                        HttpSession session,
                        HttpServletResponse response) throws UnsupportedEncodingException {
        // 获取用户输入
        String name = jReq.getString("name").trim();
        String password = jReq.getString("password").trim();

        // 查询用户信息
        User user = userService.findUserByName(name);
        if (user == null) {
            return JResp.fail("用户名不存在");
        }

        // 校验用户信息
        String pwdMd5 = SecureUtil.md5(password);
        if (!user.getPassword().equals(pwdMd5)) {
            return JResp.fail("用户名或密码错误");
        }

        // 用户登录
        this.userLogin(session, user);
        // cookie 实现自动登录
        if (AUTO_LOGIN_DAYS > 0) {
            // 对用户名中文进行 URL 编码
            String username = URLEncoder.encode(user.getName(), "UTF-8");
            // 将用户名和加密后的用户密码存入 cookie，实现七天自动登录
            Cookie usernameCookie = new Cookie(LOGIN_USERNAME, username);
            Cookie userPwdCookie = new Cookie(LOGIN_USER_PWD, user.getPassword());
            usernameCookie.setMaxAge(AUTO_LOGIN_DAYS);
            usernameCookie.setPath("/");
            userPwdCookie.setMaxAge(AUTO_LOGIN_DAYS);
            userPwdCookie.setPath("/");
            response.addCookie(usernameCookie);
            response.addCookie(userPwdCookie);
        }
        return JResp.success("登陆成功");
    }

    /**
     * 用户注销 Rest 服务
     */
    @PostMapping("/logout.do")
    @ResponseBody
    public Object logout(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        // 用户登出
        this.userLogout(session);

        // 删除 cookie 中的用户登录信息(覆盖即可删除)
        Cookie usernameCookie = new Cookie(LOGIN_USERNAME, "");
        Cookie userPwdCookie = new Cookie(LOGIN_USER_PWD, "");
        usernameCookie.setMaxAge(0);
        usernameCookie.setPath("/");
        userPwdCookie.setMaxAge(0);
        userPwdCookie.setPath("/");
        response.addCookie(usernameCookie);
        response.addCookie(userPwdCookie);
        return JResp.success("注销成功");
    }

    /**
     * 用户登录
     */
    private void userLogin(HttpSession session, User user) {
        // 根据用户 id 查询其权限
        UserAbility abilityByUserId = userAbilityService.findUserAbilityByUserId(user.getId());
        // 存入当前会话
        session.setAttribute(USER, user);
        session.setAttribute(USER_ABILITY, abilityByUserId);
    }

    /**
     * 用户登出
     */
    private void userLogout(HttpSession session) {
        // 删除 session 中的用户信息和权限信息
        session.removeAttribute(USER);
        session.removeAttribute(USER_ABILITY);
    }

}
